#ifndef LOG_H__
#define LOG_H__

#include <iostream>

// diferitele tipuri de loguri
// ERROR nu prea e corect, ca e rezervat, dar am uitat
enum LogType {
	INVISIBLE, DEBUG, INFO, WARN, FATAL
};

class Log {
public:
	// niste date referitoare la locul unde s-a produs mesajul
	Log(LogType type, const char* file_name, int line_no);

	// la iesire, face flush la cerr
	~Log();

	template<class T> friend const Log& operator << (
			const Log& log, const T& t);

private:
	LogType type() const {
		return type_;
	}

	LogType type_;
};

template<class T> const Log& operator << (
		const Log& log, const T& t) {
#ifdef NDEBUG
	if (log.type() != INVISIBLE && log.type() != DEBUG)
#else
	if (log.type() != INVISIBLE)
#endif
		std::cerr << t;

	return log;
}

Log assert(bool assertion, const char* file_name, int line_no);

// obiectul temporar creat se distruge la ;
#define LOG(type) \
	(Log(type, __FILE__, __LINE__))

#define PRINT(var) \
	(LOG(DEBUG) << #var " = " << (var))

#define ASSERT(assertion) \
	(assert(static_cast<bool>(assertion), __FILE__, __LINE__) \
	 	<< "Assertion " #assertion " failed.")

#define CHECK(left, right, op) \
	(assert((left) op (right), __FILE__, __LINE__) \
		<< "Check '" #left " " #op " " #right "' failed"\
	 	<< "\n\twhere '" << #left "' = '" << (left) \
		<< "' and '" << #right "' = '" << (right) << "'")

#define CHECK_NE(left, right) CHECK(left, right, !=)
#define CHECK_EQ(left, right) CHECK(left, right, ==)
#define CHECK_GT(left, right) CHECK(left, right, >)
#define CHECK_LT(left, right) CHECK(left, right, <)
#define CHECK_GE(left, right) CHECK(left, right, >=)
#define CHECK_LE(left, right) CHECK(left, right, <=)

#endif

